#include <windows.h>
#include <string.h>

/**
 * Redirects a function to a user definied memory location (or any named or non-named location in process memory) 
 * by patching a select number of bytes. The function will attempt to patch the requested memory
 * region, even if it is not valid. The function will change page flags on a certain memory location.
 * The function assumes x86 architecture is used.
 *
 * @param pSource: A pointer to a memory address that describes a location where redirection will occur.
 * @param pDest: A pointer to a  memory address that describes a location where the detoured function will redirect to.
 * @param lpszInstructions: A pointer to a string containing the exact hexadecimal representations of the bytes to be overwritten.
 * @param dwInstructionsLen: The amount of bytes to overwrite with redirection code.
 * @return The address of a memory region which contains code that must be executed in order to maintain the stack and avoid corruption.
 *
 * @remarks In nearly all instances, the return value will be called by the function represented by pDest. (Echo 4/17/2010 8:07 PM US EASTERN)
 */
PBYTE DetourFunction(PBYTE pSource, PBYTE pDest, LPSTR lpszInstructions, DWORD dwInstructionsLen)
{
	DWORD dwProtect;
	PBYTE pOldFunc = NULL;
	
	/* 5 bytes are needed to write the jump instruction */
	if(dwInstructionsLen > 5) return 0; 
	/* This is where the intermediate detour will go */
	if((pOldFunc = HeapAlloc(GetProcessHeap(), 0, dwInstructionsLen + 8))){
		/* Change flags on the memory so we can write and execute code */
		if(VirtualProtect(pSource, dwInstructionsLen + 5, PAGE_EXECUTE_READWRITE, &dwProtect)
			&& VirtualProtect(pOldFunc, dwInstructionsLen + 8, PAGE_EXECUTE_READWRITE, &dwProtect)){
			/* Equates to the first byte in a relative jump instruction */
			*pSource = 0xE9; 
			/* Compute the relative jump distance by subtracting the destination address from the source address then subtracting the size of 
			 * the jump instruction (5 bytes)
			 * Use a thread safe function to modify data in the process */
			InterlockedExchange((PLONG)(pSource + 1), (DWORD)((LONG)pDest - (LONG)pSource - 5));
			/* The orginal instructions that were overwritten are placed in this intermediate detour */
			memcpy(pOldFunc, lpszInstructions, dwInstructionsLen);
			/* Write the first byte of a jump to return to the original code */
			*(PBYTE)(pOldFunc + dwInstructionsLen) = 0xE9;
			/* Calculate the address that should be jumped to and use a thread safe function to write this data directly into the process */
			InterlockedExchange((PLONG)(pOldFunc + dwInstructionsLen + 1), (DWORD)((LONG)(pSource + dwInstructionsLen) - (LONG)(pOldFunc + dwInstructionsLen) - 5));
			/* Pointer to the intermediate detour code, this must be called not jumped to! */
			return pOldFunc;
		}
		HeapFree(GetProcessHeap(), 0, pOldFunc);
	}
	
	return 0;
}
